@@ -1,5 +1,6 @@ |
||
1 | 1 |
# Changes |
2 | 2 |
|
3 |
+* 0.2 (Nov 6, 2013) - PeakDetectorAgent now uses `window_duration_in_days` and `min_peak_spacing_in_days`. Additionally, peaks trigger when the time series rises over the standard deviation multiple, not after it starts to fall. |
|
3 | 4 |
* June 29, 2013 - Removed rails\_admin because it was causing deployment issues. Better to have people install their favorite admin tool if they want one. |
4 | 5 |
* June, 2013 - A number of new agents have been contributed, including interfaces to Weibo, Twitter, and Twilio, as well as Agents for translation, sentiment analysis, and for posting and receiving webhooks. |
5 | 6 |
* March 24, 2013 (0.1) - Refactored loading of Agents for `check` and `receive` to use ids instead of full objects. This should fix the too-large delayed_job issues. Added `system_timer` and `fastercsv` to the Gemfile for the Ruby 1.8 platform. |
@@ -1 +1 @@ |
||
1 |
-0.1 |
|
1 |
+0.2 |
@@ -11,9 +11,9 @@ module Agents |
||
11 | 11 |
|
12 | 12 |
Set `expected_receive_period_in_days` to the maximum amount of time that you'd expect to pass between Events being received by this Agent. |
13 | 13 |
|
14 |
- You may set `window_duration` to change the default memory window length of two weeks, |
|
15 |
- `peak_spacing` to change the default minimum peak spacing of two days, and |
|
16 |
- `std_multiple` to change the default standard deviation threshold multiple of 3. |
|
14 |
+ You may set `window_duration_in_days` to change the default memory window length of `14` days, |
|
15 |
+ `min_peak_spacing_in_days` to change the default minimum peak spacing of `2` days (peaks closer together will be ignored), and |
|
16 |
+ `std_multiple` to change the default standard deviation threshold multiple of `3`. |
|
17 | 17 |
MD |
18 | 18 |
|
19 | 19 |
event_description <<-MD |
@@ -61,27 +61,22 @@ module Agents |
||
61 | 61 |
memory[:peaks][group] ||= [] |
62 | 62 |
|
63 | 63 |
if memory[:data][group].length > 4 && (memory[:peaks][group].empty? || memory[:peaks][group].last < event.created_at.to_i - peak_spacing) |
64 |
- average_value, standard_deviation = stats_for(group, :skip_last => 2) |
|
65 |
- newest_value = memory[:data][group][-1].first.to_f |
|
66 |
- second_newest_value, second_newest_time = memory[:data][group][-2].map(&:to_f) |
|
67 |
- |
|
68 |
- #pp({:newest_value => newest_value, |
|
69 |
- # :second_newest_value => second_newest_value, |
|
70 |
- # :average_value => average_value, |
|
71 |
- # :standard_deviation => standard_deviation, |
|
72 |
- # :threshold => average_value + std_multiple * standard_deviation }) |
|
73 |
- |
|
74 |
- if newest_value < second_newest_value && second_newest_value > average_value + std_multiple * standard_deviation |
|
75 |
- memory[:peaks][group] << second_newest_time |
|
76 |
- memory[:peaks][group].reject! { |p| p <= second_newest_time - window_duration } |
|
77 |
- create_event :payload => {:message => options[:message], :peak => second_newest_value, :peak_time => second_newest_time, :grouped_by => group.to_s} |
|
64 |
+ average_value, standard_deviation = stats_for(group, :skip_last => 1) |
|
65 |
+ newest_value, newest_time = memory[:data][group][-1].map(&:to_f) |
|
66 |
+ |
|
67 |
+ #p [newest_value, average_value, average_value + std_multiple * standard_deviation, standard_deviation] |
|
68 |
+ |
|
69 |
+ if newest_value > average_value + std_multiple * standard_deviation |
|
70 |
+ memory[:peaks][group] << newest_time |
|
71 |
+ memory[:peaks][group].reject! { |p| p <= newest_time - window_duration } |
|
72 |
+ create_event :payload => {:message => options[:message], :peak => newest_value, :peak_time => newest_time, :grouped_by => group.to_s} |
|
78 | 73 |
end |
79 | 74 |
end |
80 | 75 |
end |
81 | 76 |
|
82 | 77 |
def stats_for(group, options = {}) |
83 | 78 |
data = memory[:data][group].map { |d| d.first.to_f } |
84 |
- data = data[0...(memory[:data][group].length - (options[:skip_last] || 0))] |
|
79 |
+ data = data[0...(data.length - (options[:skip_last] || 0))] |
|
85 | 80 |
length = data.length.to_f |
86 | 81 |
mean = 0 |
87 | 82 |
mean_variance = 0 |
@@ -99,15 +94,23 @@ module Agents |
||
99 | 94 |
end |
100 | 95 |
|
101 | 96 |
def window_duration |
102 |
- (options[:window_duration].present? && options[:window_duration].to_i) || 2.weeks |
|
97 |
+ if options[:window_duration].present? # The older option |
|
98 |
+ options[:window_duration].to_i |
|
99 |
+ else |
|
100 |
+ (options[:window_duration_in_days] || 14).to_f.days |
|
101 |
+ end |
|
103 | 102 |
end |
104 | 103 |
|
105 | 104 |
def std_multiple |
106 |
- (options[:std_multiple].present? && options[:std_multiple].to_i) || 3 |
|
105 |
+ (options[:std_multiple] || 3).to_f |
|
107 | 106 |
end |
108 | 107 |
|
109 | 108 |
def peak_spacing |
110 |
- (options[:peak_spacing].present? && options[:peak_spacing].to_i) || 2.days |
|
109 |
+ if options[:peak_spacing].present? # The older option |
|
110 |
+ options[:peak_spacing].to_i |
|
111 |
+ else |
|
112 |
+ (options[:min_peak_spacing_in_days] || 2).to_f.days |
|
113 |
+ end |
|
111 | 114 |
end |
112 | 115 |
|
113 | 116 |
def group_for(event) |
@@ -36,7 +36,7 @@ describe Agents::PeakDetectorAgent do |
||
36 | 36 |
end |
37 | 37 |
|
38 | 38 |
it "keeps a rolling window of data" do |
39 |
- @agent.options[:window_duration] = 5.hours |
|
39 |
+ @agent.options[:window_duration_in_days] = 5/24.0 |
|
40 | 40 |
@agent.receive build_events(:keys => [:count], |
41 | 41 |
:values => [1, 2, 3, 4, 5, 6, 7, 8].map {|i| [i]}, |
42 | 42 |
:pattern => { :filter => "something" }) |
@@ -47,14 +47,14 @@ describe Agents::PeakDetectorAgent do |
||
47 | 47 |
build_events(:keys => [:count], |
48 | 48 |
:values => [5, 6, |
49 | 49 |
4, 5, |
50 |
- 8, 11, |
|
50 |
+ 4, 5, |
|
51 | 51 |
15, 11, # peak |
52 |
- 8, 5, |
|
52 |
+ 8, 50, # ignored because it's too close to the first peak |
|
53 | 53 |
4, 5].map {|i| [i]}, |
54 | 54 |
:pattern => { :filter => "something" }).each.with_index do |event, index| |
55 | 55 |
lambda { |
56 | 56 |
@agent.receive([event]) |
57 |
- }.should change { @agent.events.count }.by( index == 7 ? 1 : 0 ) |
|
57 |
+ }.should change { @agent.events.count }.by( index == 6 ? 1 : 0 ) |
|
58 | 58 |
end |
59 | 59 |
|
60 | 60 |
@agent.events.last.payload[:peak].should == 15.0 |
@@ -62,10 +62,9 @@ describe Agents::PeakDetectorAgent do |
||
62 | 62 |
end |
63 | 63 |
|
64 | 64 |
it "keeps a rolling window of peaks" do |
65 |
- @agent.options[:window_duration] = 5.hours |
|
66 |
- @agent.options[:peak_spacing] = 1.hour |
|
65 |
+ @agent.options[:min_peak_spacing_in_days] = 1/24.0 |
|
67 | 66 |
@agent.receive build_events(:keys => [:count], |
68 |
- :values => [1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 10, 1, 1, 1, 10, 1].map {|i| [i]}, |
|
67 |
+ :values => [1, 1, 1, 1, 1, 1, 10, 1, 1, 1, 1, 1, 1, 1, 10, 1].map {|i| [i]}, |
|
69 | 68 |
:pattern => { :filter => "something" }) |
70 | 69 |
@agent.memory[:peaks][:something].length.should == 2 |
71 | 70 |
end |